#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/select.h>
#include <sys/time.h>

#include <queue>

#include <iomanip>
#include <openssl/md5.h>

//#include "paquetes.h"
#include "funciones.h"
#include "clases.h"

int main(int argc, char **argv)
{
	/* control del tiempo */
	struct timeval tv;
	fd_set readfds;
	tv.tv_sec = 0;
	tv.tv_usec = 10000000;

	FD_ZERO(&readfds);

	/* fin control del tiempo */
	int sock = socket(PF_INET, SOCK_DGRAM, 0);
	//std::cout << "Se obtuvo el socket " << sock << std::endl;
	struct sockaddr_in my_addr;
	struct sockaddr_in from;
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(5000);
	my_addr.sin_addr.s_addr = inet_addr("192.168.0.101");
	memset(my_addr.sin_zero, '\0', sizeof(my_addr.sin_zero));
	int yes = 1;    
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) 
	{
	perror("setsockopt");
	exit(1);
	}     
	int ret = bind(sock, (struct sockaddr *)&my_addr, sizeof(my_addr));    
	std::cout << "Bind retorna " << ret << std::endl;
	if(ret<0)
	{
			perror("Error de bind:");
	}
	socklen_t fromlen=sizeof(struct sockaddr_in);
	byte *dato;
	int estado=0,n=0;
	struct PaqueteVariable p;
	char checksum[33];
	bool ACK=false;
	std::string dato1;	
	char *auxiliar;
	int testtime=0;
	Circulo C1;
	Rectangulo R1;	
	while(1)
	{
		switch(estado)
		{
			case 0:
				/**
				 * Se recive un paquete 
				 * 
				 */
                    std::cout << "Esperando Paquete" << std::endl;
					n = recvfrom(sock,&p,sizeof(struct PaqueteVariable),0,(struct sockaddr *)&from,&fromlen);
					std::cout << n << std::endl;
					if (n < 0)
					{
						perror("RecvFrom");
					}else
					{
						estado = 1;
						n = 0;
						ACK=true;
						std::cout << "-PKTSYN PAQUETE RECIBIDO- successful" << std::endl;
					}
	
			break;
			
			case 1:
				/**
				 * Se Acusa recivo
				 * 
				 */
				n=sendto(sock,&ACK,sizeof(bool),0,(struct sockaddr *)&from,fromlen);
				//n=sendto(sock,&ACK,sizeof(bool),0,(struct sockaddr *)&from,fromlen);
				if(n<0)
				{
					perror("Sendto");
				}else
				{
					std::cout << "-PKTSEND ACK DEL PAQUETE RECIBIDO- successful" << std::endl;
					estado = 2;
					n = 0;
					ACK=false;
				}				
			break;
			
			case 2:
				/**
				 *   Se recive el dato
				 * */
				dato=new byte[p.largo];
				
				FD_ZERO(&readfds);
				FD_SET(sock, &readfds);
				select(sock+1, &readfds, NULL, NULL, &tv); 

				if (FD_ISSET(sock, &readfds))
				{
					n=recvfrom(sock,dato,p.largo,0,(struct sockaddr *)&from,&fromlen);
					if(n<0)
					{
						perror("Recvfrom");
					}else
					{
						checksum[33]='\0';
						md5(dato,p.largo,checksum);
						dato[n]='\0';
						if(p.numeroMagico == NUMERO_MAGICO)
						{
							std::cout << strlen(checksum) << strlen(p.checksum) << std::endl;
							std::cout << checksum << std::endl;
							std::cout << p.checksum << std::endl;
							std::cout << dato << std::endl;
							std::cout << strlen((char*)dato) << std::endl;
							if(strcmp(p.checksum,checksum)==0)
							{
								std::cout << "-PKTRECV DATO RECIBIDO- successful" << std::endl;
								estado=3;
								n=0;
								ACK=true;
							}else
							{
								std::cout << "-PKTRECV DATO RECIBIDO- unsuccessful" << std::endl;
								estado=3;
								n=0;
								ACK=false;
							}	
						}else
						{
							std::cout << "-PKTRECV DATO RECIBIDO- unsuccessful" << std::endl;
							estado=3;
							n=0;
							ACK=false;
						}
					}	
				}else
				{
					std::cout << "TIME OUT " << std::endl;
					ACK = false;
					estado=3;
				}
				
			break;
			case 3:
				/**
				 * Se Acusa recibo
				 * 
				 */
				n=sendto(sock,&ACK,sizeof(bool),0,(struct sockaddr *)&from,fromlen);
				if(n<0)
				{
					perror("Sendto");
				}else
				{
					std::cout << "-PKTSEND ACK DEL DATO RECIBIDO- successful" << std::endl;
					if(ACK==true)
					{
						estado=4;
						n=0;
					}
					if(ACK==false)
					{
						estado=0;
						n=0;
					}
				}				
			break;
			case 4:
				/**
				* Procesar dato;
				*/
			    if(strcmp((char*)dato,"funciones?")==0)
				{
					dato1 ="Rectangulo,(int)perimetro,(int)ancho,(int)largo\nRectangulo,(int)area,(int)ancho,(int)largo\nCirculo,(float)perimetro,(float)radio\nCirculo,(float)area,(float)radio\n"/*Triangulo,(int)perimero,(int)base,(int)ladoa,(int)ladob,(int)alto*/;//sacar esto de un archivo;
				}
				else if(strncmp((char*)dato,"Rectangulo,(int)perimetro,",26)==0) /* perimetro */
				{
					auxiliar = strtok ((char*)dato,",");
					int i=0;
					while (auxiliar != NULL)
					{
						std::cout << auxiliar << std::endl;						
						i++;
						auxiliar = strtok (NULL, ",");						
						if(i==2)
						{
							R1.setancho(atoi(auxiliar));
						}else if(i==3 && auxiliar != NULL)
						{
							R1.setlargo(atoi(auxiliar));
						}
					}
					if(i==4)
					{
						dato1=int2string(R1.perimetro());
					}
					else
					{
						dato1="ENVIO MAL LOS DATOS PARA EL RECTANGULO\n";
					}
					i=0;
					
				}else if(strncmp((char*)dato,"Rectangulo,(int)area,",21)==0) /* perimetro */
				{
					
					auxiliar = strtok ((char*)dato,",");
					int i=0;
					while (auxiliar != NULL)
					{
						std::cout << auxiliar << std::endl;
						i++;	
						auxiliar = strtok (NULL, ",");
						
						if(i==2)
						{
							R1.setancho(atoi(auxiliar));
						}else if(i==3 && auxiliar != NULL)
						{
							R1.setlargo(atoi(auxiliar));
						}
					}
					if(i==4)
					{
						dato1=int2string(R1.area());
					}
					else
					{
						dato1="ENVIO MAL LOS DATOS PARA EL RECTANGULO\n";
					}
					i=0;
				}
/*				Circulo,(float)perimetro,(float)radio
Circulo,(float)area,(float)radio  */

				else if(strncmp((char*)dato,"Circulo,(float)perimetro,",25)==0) /* perimetro */
				{
					
                   	char *auxiliar;
					auxiliar = strtok ((char*)dato,",");
					int i=0;
					while (auxiliar != NULL)
					{
						std::cout << auxiliar << std::endl;						
						i++;
						auxiliar = strtok (NULL, ",");
						if(i==2 && auxiliar != NULL)
						{
							C1.setradio(atof(auxiliar));							
						}
					}
					if(i==3)
					{
						dato1=float2string(C1.perimetro());
					}else
					{
						dato1="ENVIO MAL LOS DATOS PARA EL RECTANGULO\n";						
					}
					i=0;
				}
				else if(strncmp((char*)dato,"Circulo,(float)area,",18)==0) /* perimetro */
				{
					char *auxiliar;
					auxiliar = strtok ((char*)dato,",");
					int i=0;
					while (auxiliar != NULL)
					{
						std::cout << auxiliar << std::endl;
						i++;
						auxiliar = strtok (NULL, ",");
						if(i==2)
						{
							C1.setradio(atof(auxiliar));
						}
							
					}
					if(i==3)
					{
						dato1=float2string(C1.area());
					}else
					{
						dato1="ENVIO MAL LOS DATOS PARA EL RECTANGULO\n";						
					}
					i=0;
				}
				else
				{
					dato1="Objeto o metodo desconocido, si desea saber que objetos o metodos estan disponibles envie funciones? \n";
				}
				p.numeroMagico=NUMERO_MAGICO;
				p.largo=dato1.size();
				std::cout << p.largo << std::endl;
				std::cout << dato1 << std::endl;
				md5(dato1.c_str(),p.checksum);
				estado=5;
			break;
			case 5:
                /**
				* Enviar paquete de dato 1;
				*/
				n=sendto(sock,&p,sizeof(struct PaqueteVariable),0,(struct sockaddr *)&from,fromlen);
				if(n<0)
				{
					perror("SendTo");
				}
				else{
					std::cout << "-PKTSEND ENVIAR PAQUETE DE LA RESPUESTA - successful" << std::endl;
					estado=6;
					n=0;
				}
			break;
			case 6:
				/**
				 * Recibir ACK -------------tiempo!!
				 */
                 n = recvfrom(sock,&ACK,sizeof(bool),0,(struct sockaddr *)&from,&fromlen);
				if(n < 0 )
				{
					perror("Recvfrom");
				}else
				{
					
					if(ACK == true)
					{
						std::cout << "-PKTACK RECIBIR ACK DE LA RESPUESTA- successful" << std::endl;
						estado = 7;
					}else if(ACK == false)
					{
						std::cout << "-PKTACK RECIBIR ACK DE LA RESPUESTA- unsuccessful" << std::endl;
						estado = 5;
					}					
				}
			break;
			case 7:
				/**
				* Enviar Dato1
				*/
				n = sendto(sock,serializarString(dato1.c_str()),p.largo,0,(struct sockaddr *)&from,fromlen);
				if (n < 0)
				{
					perror("Envio");
				}else
				{
					std::cout << "-PKTSEND DATO ENVIAR DATO DE LA RESPUESTA- successful" << std::endl;
					estado = 8;
					n = 0;
				}
			break;
			case 8:
                  /**
				 * Recibir ACK 
				 */
                n = recvfrom(sock,&ACK,sizeof(bool),0,(struct sockaddr *)&from,&fromlen);
				if(n < 0 )
				{
					perror("Recvfrom");
				}else
				{
					
					if(ACK == true)
					{
						std::cout << "-PKTACK RECIBIR ACK DE LA RESPUESTA- successful" << std::endl;
						estado = 9;
					}else if(ACK == false)
					{
						std::cout << "-PKTACK RECIBIR ACK DE LA RESPUESTA- unsuccessful" << std::endl;
						estado = 5;
					}					
				}
			break;
			case 9:
				dato1.clear();				
				delete dato;
				estado=0;
			break;
			default:
			     std::cout << "-D- ESTA EN EL DEFAULT" << std::endl;
			break;
		}
	}
	return 0;
}
